﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Audio;
using Pigs.Weapons;
using Microsoft.Xna.Framework.Content;

namespace pigs
{
    public class Player : pigs.DynamicObject
    {
        public CrossHair aimer;
        public Man man;
        public float playerSpeed;
        public Texture2D bulletTexture;
        public Texture2D flameTexture;
        public Texture2D manTexture;
        public Texture2D bombTexture;
        public Texture2D explosionTexture;

        SoundEffect laserSound;
        int frameCount = 0; // Current frame
        int frameSkipX = 89; // Width of a frame
        int animationCount; // How many ticks since the last frame change.
        int animationMax; // How many ticks to change frame after. 
        Rectangle source;

        public int score;
        public int accuracy;

        public List<Bullet> bullets;
        public List<Flame> flames;
        public List<Bomb> bombs;
        public List<Explosion> explosions;
        public int pigsHit;
        public int shotsFired;

        // Whether or not the player is visible
        public bool visible;

        bool autoFire;
        public int fireMode;
        public TimeSpan minShootTime;

        public TimeSpan prevShootTime;
        public TimeSpan prevSlopTime;
        public TimeSpan minSlopTime;

        int XMIN, XMAX, YMIN, YMAX;
        public Boolean alive;
        float damageMultiplier;

        // If you would die, set a timer to make you uncolideable for 2 seconds
        public bool reset;
        public TimeSpan resetTimer;

        Boolean playSound;
        public Boolean notHit;
        public int deathCouth;

        /// <summary>
        /// create the player
        /// </summary>
        /// <param name="texture">image for the player</param>
        /// <param name="position">where the player is</param>
        public Player(Texture2D texture, Vector2 position)
            : base(texture, position)
        {
        }

        /// <summary>
        /// initialize the player with all the images, speed/damage factors, and if they should play sound
        /// </summary>
        public void Initialize(Texture2D aimerTexture, Vector2 aimerPosition, ContentManager content, float damageFactor, float speed, Boolean sound)
        {
            laserSound = content.Load<SoundEffect>("sounds/mp5");
            bulletTexture = content.Load<Texture2D>("weapons/bullet");
            flameTexture = content.Load<Texture2D>("weapons/fireball");
            manTexture = content.Load<Texture2D>("player/man");
            bombTexture = content.Load<Texture2D>("weapons/bomb2");
            explosionTexture = content.Load<Texture2D>("weapons/explosion");
            playSound = sound;

            // Start with frame 1 of image array
            this.frameCount = 1;
            this.animationCount = 0;
            this.animationMax = 6;
            health = 100;
            playerSpeed = 3f * speed; //1.5f
            damage = 100;
            score = 0;
            visible = true;

            damageMultiplier = damageFactor;

            this.source = new Rectangle(this.frameSkipX * this.frameCount, 0, this.frameSkipX, this.texture.Height);

            //get the color data
            colorMap = new Color[source.Width * source.Height];
            this.texture.GetData(0, source, colorMap, 0, source.Width * source.Height);

            rectangle = new Rectangle((int)this.position.X, (int)this.position.Y, source.Width, this.texture.Height);
            aimer = new CrossHair();
            aimer.Initialize(aimerTexture, aimerPosition);

            man = new Man();
            man.Initialize(manTexture, new Vector2(144,151), 25);

            bullets = new List<Bullet>();
            bombs = new List<Bomb>();
            flames = new List<Flame>();
            explosions = new List<Explosion>();

            minShootTime = TimeSpan.FromSeconds(.15f); //.15f
            minSlopTime = TimeSpan.FromSeconds(3.50f);
            prevSlopTime = TimeSpan.FromSeconds(-3.50f);

            autoFire = false;

            fireMode = 0;

            pigsHit = 0;
            shotsFired = 0;
            accuracy = 100;

            this.active = true;
            this.alive = true;
            this.notHit = true;
            this.deathCouth = 0;
        }

        public void Update(MouseState mouse, GameTime gametime)
        {
            aimer.Position = new Vector2(mouse.X, mouse.Y);
            if (mouse.LeftButton == ButtonState.Pressed) { fireBullet(gametime); }
        }

        /// <summary>
        /// set bounds for the player so they can't go off screen
        /// </summary>
        /// <param name="minX">min x value the player can go</param>
        /// <param name="maxX">max x value the player can go</param>
        /// <param name="minY">min y value the player can go</param>
        /// <param name="maxY">max y value the player can go</param>
        public void setBounds(int minX, int maxX, int minY, int maxY)
        {
            XMAX = maxX;
            XMIN = minX;
            YMAX = maxY;
            YMIN = minY;
        }

        /// <summary>
        /// update the player from the gamepad
        /// </summary>
        /// <param name="time">current game time</param>
        /// <param name="currentGamePadState">current gamepad state</param>
        /// <param name="prevGamePadState">previous gamepad state</param>
        public void Update(GameTime time, GamePadState currentGamePadState, GamePadState prevGamePadState, GameSettings settings)
        {
            this.playSound = settings.playSound;

            // update position from left gamepad Thumbstick Controls
            this.position.X += currentGamePadState.ThumbSticks.Left.X * playerSpeed;
            this.position.Y -= currentGamePadState.ThumbSticks.Left.Y * playerSpeed;
           
            aimer.Update(currentGamePadState);

            //If X is down, turn on autofire
            if (currentGamePadState.IsButtonDown(Buttons.X) && !prevGamePadState.IsButtonDown(Buttons.X))
            {
                autoFire = !autoFire;
            }
            //should we fire a bullet
            if (currentGamePadState.IsButtonDown(Buttons.RightTrigger) || autoFire)
            {
                fireBullet(time);
            }
        }

        /// <summary>
        /// update the player from the keyboard
        /// </summary>
        /// <param name="time">current gametime</param>
        /// <param name="currentKeyboardState">current keyboard state</param>
        /// <param name="prevKeyboardState">preivous gamepad state</param>
        public void Update(GameTime time, KeyboardState currentKeyboardState, KeyboardState prevKeyboardState, GameSettings settings)
        {
            this.playSound = settings.playSound;

            //If k is down, turn on autofire
            if (currentKeyboardState.IsKeyDown(Keys.K) && !prevKeyboardState.IsKeyDown(Keys.K))
            {
                if (autoFire)
                {
                    autoFire = false;
                }
                else
                {
                    autoFire = true;
                }
            }

            // update position from keyboard wasd
            if (currentKeyboardState.IsKeyDown(Keys.A))
            {
                this.position.X -= playerSpeed;
                this.animationCount += 1;
            }
            if (currentKeyboardState.IsKeyDown(Keys.D))
            {
                this.position.X += playerSpeed;
                this.animationCount += 1;
            }
            if (currentKeyboardState.IsKeyDown(Keys.S))
            {
                this.position.Y += playerSpeed;
            }
            if (currentKeyboardState.IsKeyDown(Keys.W))
            {
                this.position.Y -= playerSpeed;
            }

            aimer.Update(currentKeyboardState);

            //should we fire a bullet
            if (currentKeyboardState.IsKeyDown(Keys.Space) || autoFire)
            {
                fireBullet(time);
            }
        }

        /// <summary>
        /// update stuff that's common to gamepad and keyboard methods
        /// </summary>
        /// <param name="time"></param>
        public void finishUpdate(GameTime time)
        {
            for (int i = 0; i < bombs.Count; i++ )
            {
                if (bombs[i].timer <= 0) this.ExplodeBomb(bombs[i]);
                if (!bombs[i].rectangle.Intersects(new Rectangle(50, 80, XMAX - 50 - bombs[i].Width, YMAX - 50 - bombs[i].Width))) this.ExplodeBomb(bombs[i]);
            }

            if (shotsFired > 0)
            {
                accuracy = 100 * pigsHit / shotsFired;
            }

            rectangle = new Rectangle((int)this.position.X, (int)this.position.Y, source.Width, this.texture.Height);

            // Have player and aimer remain in bounds
            position.X = MathHelper.Clamp(position.X, XMIN, XMAX - this.rectangle.Width );
            position.Y = MathHelper.Clamp(position.Y, YMIN, YMAX - this.rectangle.Height);
            aimer.Position.X = MathHelper.Clamp(aimer.Position.X, XMIN, XMAX - this.aimer.Width);
            aimer.Position.Y = MathHelper.Clamp(aimer.Position.Y, YMIN, YMAX - this.aimer.Height);

            rectangle = new Rectangle((int)this.position.X, (int)this.position.Y, source.Width, source.Height);

            man.Position.X = this.position.X + 44 + this.man.Texture.Width/2;
            man.Position.Y = this.position.Y + 51 + this.man.Texture.Height/2;

            Vector2 startPosition = new Vector2(this.position.X + this.rectangle.Width, Height / 2 + this.position.Y);
            float xDist = aimer.Position.X + aimer.Width / 2 - startPosition.X;
            float yDist = aimer.Position.Y + aimer.Height / 2 - startPosition.Y;

            Vector2 angle = new Vector2(Math.Abs(xDist) / (Math.Abs(xDist) + Math.Abs(yDist)), yDist / (Math.Abs(xDist) + Math.Abs(yDist)));

            if (xDist < 0)
            {
                man.Update(new Vector2(angle.X, -1* angle.Y));
                man.spriteeffect = SpriteEffects.FlipHorizontally;
            }
            else
            {
                man.Update(angle);
                man.spriteeffect = SpriteEffects.None;
            }
            UpdateAnimation();

        }

        /// <summary>
        /// update the players bullet locations
        /// </summary>
        public void updateBullets()
        {
            foreach (Bullet b in bullets) b.Update();
            foreach (Flame f in flames)  f.Update();
            foreach (Bomb b in bombs) b.Update();
            foreach (Explosion e in explosions) e.Update();
        }

        public void bombAddBullets(int x, int y, Bomb bomb)
        {
            float xDist = x;
            float yDist = y;

            //make a new bullet
            Vector2 angle = new Vector2(xDist / (Math.Abs(xDist) + Math.Abs(yDist)), yDist / (Math.Abs(xDist) + Math.Abs(yDist)));

            //add the bullet with it's data
            Bullet b = new Bullet(bulletTexture, bomb.center());
            b.Initialize(angle);
            bullets.Add(b);

        }

        public void ExplodeBomb(Bomb bomb)
        {
            Explosion e = new Explosion(explosionTexture, bomb.center(), 1.3f);
            explosions.Add(e);

            bombAddBullets(100, 0, bomb);
            bombAddBullets(100, 100, bomb);
            bombAddBullets(0, 100, bomb);
            bombAddBullets(-100, 100, bomb);
            bombAddBullets(-100, 0, bomb);
            bombAddBullets(-100, -100, bomb);
            bombAddBullets(0, -100, bomb);
            bombAddBullets(100, -100, bomb);

            bomb.active = false;

        }

        public void applyDamage(float d)
        {
            this.notHit = false;
            this.health = (health - d * damageMultiplier);
        }

        /// <summary>
        /// fire a new bullet from the player
        /// </summary>
        /// <param name="time">current game time</param>
        public void fireBullet(GameTime time)
        {
            if (alive)
            {
                //If the player is firing regular bullets
                //has the time since the last bullet fired been long enough to fire another?
                if ((time.TotalGameTime - prevShootTime) > minShootTime)
                {
                    //get the angle at which to fire it
                    Vector2 startPosition = new Vector2(this.position.X + 44 + this.man.Texture.Width / 2, this.position.Y + 55 + this.man.Texture.Height / 2);
                    float xDist = aimer.Position.X + aimer.Width / 2 - startPosition.X;
                    float yDist = aimer.Position.Y + aimer.Height / 2 - startPosition.Y;

                    if (fireMode == 0)
                    {
                        //make a new bullet
                        Vector2 angle = new Vector2(xDist / (Math.Abs(xDist) + Math.Abs(yDist)), yDist / (Math.Abs(xDist) + Math.Abs(yDist)));

                        //add the bullet with it's data
                        Bullet b = new Bullet(bulletTexture, startPosition);
                        b.Initialize(angle);
                        bullets.Add(b);

                        if (playSound) laserSound.Play();
                        this.shotsFired++;
                    }
                    //If the player has a tri-shot upgrade
                    else if (fireMode == 1)
                    {
                        Vector2 control = new Vector2(xDist / (Math.Abs(xDist) + Math.Abs(yDist)), yDist / (Math.Abs(xDist) + Math.Abs(yDist)));

                        double angle;

                        angle = Math.Atan2(yDist, xDist);
                        angle = angle * -1;

                        float newX = aimer.Position.X + (float)((Math.Sin(angle)) * 25);
                        float newY = aimer.Position.Y + (float)((Math.Cos(angle)) * 25);
                        float newX2 = aimer.Position.X - (float)((Math.Sin(angle)) * 25);
                        float newY2 = aimer.Position.Y - (float)((Math.Cos(angle)) * 25);

                        float xDist2 = newX + aimer.Width / 2 - startPosition.X;
                        float yDist2 = newY + aimer.Height / 2 - startPosition.Y;
                        float xDist3 = newX2 + aimer.Width / 2 - startPosition.X;
                        float yDist3 = newY2 + aimer.Height / 2 - startPosition.Y;
                        Vector2 test1 = new Vector2(xDist2 / (Math.Abs(xDist2) + Math.Abs(yDist2)), yDist2 / (Math.Abs(xDist2) + Math.Abs(yDist2)));
                        Vector2 test2 = new Vector2(xDist3 / (Math.Abs(xDist3) + Math.Abs(yDist3)), yDist3 / (Math.Abs(xDist3) + Math.Abs(yDist3)));

                        Bullet b1 = new Bullet(bulletTexture, startPosition);
                        Bullet b2 = new Bullet(bulletTexture, startPosition);
                        Bullet b3 = new Bullet(bulletTexture, startPosition);

                        b1.Initialize(control);
                        b2.Initialize(test1);
                        b3.Initialize(test2);
                        bullets.Add(b1);
                        bullets.Add(b2);
                        bullets.Add(b3);

                        if (playSound) laserSound.Play();
                        this.shotsFired += 3;
                    }
                    else if (fireMode == 2)
                    {
                        //make a new bullet
                        Vector2 angle = new Vector2(xDist / (Math.Abs(xDist) + Math.Abs(yDist)), yDist / (Math.Abs(xDist) + Math.Abs(yDist)));

                        //add the bullet with it's data
                        Bomb b = new Bomb(bombTexture, startPosition);
                        b.Initialize(angle);
                        bombs.Add(b);

                        this.shotsFired += 4;
                    }
                    else if (fireMode == 3)
                    {
                        Vector2 control = new Vector2(xDist / (Math.Abs(xDist) + Math.Abs(yDist)), yDist / (Math.Abs(xDist) + Math.Abs(yDist)));

                        double angle;

                        angle = Math.Atan2(yDist, xDist);
                        angle = angle * -1;

                        float newX = aimer.Position.X + (float)((Math.Sin(angle)) * 25);
                        float newY = aimer.Position.Y + (float)((Math.Cos(angle)) * 25);
                        float newX2 = aimer.Position.X - (float)((Math.Sin(angle)) * 25);
                        float newY2 = aimer.Position.Y - (float)((Math.Cos(angle)) * 25);

                        float xDist2 = newX + aimer.Width / 2 - startPosition.X;
                        float yDist2 = newY + aimer.Height / 2 - startPosition.Y;
                        float xDist3 = newX2 + aimer.Width / 2 - startPosition.X;
                        float yDist3 = newY2 + aimer.Height / 2 - startPosition.Y;
                        Vector2 test1 = new Vector2(xDist2 / (Math.Abs(xDist2) + Math.Abs(yDist2)), yDist2 / (Math.Abs(xDist2) + Math.Abs(yDist2)));
                        Vector2 test2 = new Vector2(xDist3 / (Math.Abs(xDist3) + Math.Abs(yDist3)), yDist3 / (Math.Abs(xDist3) + Math.Abs(yDist3)));

                        Flame f1 = new Flame(flameTexture, startPosition);
                        Flame f2 = new Flame(flameTexture, startPosition);
                        Flame f3 = new Flame(flameTexture, startPosition);

                        f1.Initialize(control);
                        f2.Initialize(test1);
                        f3.Initialize(test2);
                        flames.Add(f1);
                        flames.Add(f2);
                        flames.Add(f3);

                        this.shotsFired += 3;
                    }
                    //update prevshoottime
                    prevShootTime = time.TotalGameTime;
                }
            }
        }
        
        /// <summary>
        /// draw the players bullets
        /// </summary>
        /// <param name="spriteBatch">spritebatch that should draw the bullets</param>
        public void drawBullets(SpriteBatch spriteBatch)
        {            
            //draw the bullets
            foreach (Bullet b in bullets) { b.Draw(spriteBatch); }
            foreach (Bomb b in bombs) { b.Draw(spriteBatch); }
            foreach (Explosion e in explosions) { e.Draw(spriteBatch); }
        }

        /// <summary>
        /// draw the player
        /// </summary>
        /// <param name="spriteBatch">spritebatch to draw the player from</param>
        public override void Draw(SpriteBatch spriteBatch)
        {
            if (alive)
            {
                //spriteBatch.Draw(this.texture, this.position, this.source, Color.White, 0f, Vector2.Zero, 1f, SpriteEffects.None, 0f);
                if (visible)
                {
                    spriteBatch.Draw(this.texture, this.rectangle, this.source, Color.White);
                    man.Draw(spriteBatch);
                }
                aimer.Draw(spriteBatch);
            }
            foreach (Flame f in flames) { f.Draw(spriteBatch); }
        }

        /// <summary>
        /// update the player animation
        /// </summary>
        public void UpdateAnimation()
        {
            this.animationCount += 1;
            if (this.animationCount > this.animationMax)
            {
                this.animationCount = 0;
                this.frameCount += 1;

                if (this.frameCount == 4){ this.frameCount = 0; }

                // Update the source rectangle, based on where in the animation we are.  
                this.source.X = this.frameSkipX * this.frameCount;

                //get the color data
                colorMap = new Color[source.Width * source.Height];
                this.texture.GetData(0, source, colorMap, 0, source.Width * source.Height);
            }
        }

        public void removeOldBullets()
        {
            for (int i = 0; i < bullets.Count; i++)
            {
                //check if bullet's gone off viewing area
                if (!bullets[i].active || bullets[i].position.X < -50 || bullets[i].position.X > XMAX + 50
                    || bullets[i].position.Y < -50 || bullets[i].position.Y > YMAX + 50)
                {
                    bullets.RemoveAt(i);
                }
            }
            for (int i = 0; i < flames.Count; i++)
            {
                //check if bullet's gone off viewing area
                if (!flames[i].active)
                {
                   flames.RemoveAt(i);
                }
            }

            for (int i = 0; i < bombs.Count; i++)
            {
                if (bombs.Count > 0 && !bombs[i].active)
                {
                    bombs.RemoveAt(i);
                }
            }

            for (int i = 0; i < explosions.Count; i++)
            {
                if (explosions.Count > 0 && !explosions[i].active)
                {
                    explosions.RemoveAt(i);
                }
            }
        }
    }
}
